#ifndef WOLF_ACTOR_HPP
#define WOLF_ACTOR_HPP

#include "common.hpp"
#include "layer.hpp"
#include "sprite.hpp"
#include "rect.hpp"
#include <vector>
#include <string>
#include "ActorHandlers.hpp"

namespace wolf {

/**
 * @brief Represents an character or object on screen
 * @ingroup actorsMod
 *
 *
 * This class provides an common interface for a character and some useful methods.
 *  This class expects to be called from a EventHandler.
 * It supports animation (provided by sprites) and
 **/
class WOLF_DECLSPEC Actor: public Layer {
public:
	/**
	 * @brief Creates an actor on given position
	 *
	 *
	 **/
	Actor(const Sprite &sprite, int x = 0, int y = 0, int depth = 0);

	/**
	 * @brief Creates an actor on given position from a file location. It uses SDL_Image internally.
	 */
	Actor(const string &file, Uint16 nCollumns = 1, Uint16 nRows = 1, int x = 0,
			int y = 0, int depth = 0);

	/**
	 * @brief Creates an actor on given position using a surface.
	 * The surface is @b not copied, but shared. It is like a sprite creation with own = false.
	 */
	Actor(SDL_Surface *src, Uint16 nCollumns = 1, Uint16 nRows = 1, int x = 0,
			int y = 0, int depth = 0);

	/**
	 * @brief Creates an actor on given position from a RW_ops. The src is @ not freed.
	 */
	Actor(SDL_RWops *src, Uint16 nCollumns = 1, Uint16 nRows = 1, int x = 0,
			int y = 0, int depth = 0);

	/**
	 * @brief Destroys the actor
	 *
	 *
	 **/
	virtual ~Actor(void);

	/**
	 * @brief Gets the sprite
	 *
	 *
	 **/
	const Sprite & getSprite(void) const;

	/**
	 * @brief Gets the sprite in modifiable version
	 *
	 *
	 **/
	Sprite & getSprite(void);

	/**
	 * @brief Sets the sprite
	 *
	 *
	 **/
	void setSprite(const Sprite &sprite);

	/**
	 * @brief Gets the x axis
	 *
	 *
	 **/
	int getX(void) const;

	/**
	 * @brief Sets the x axis
	 *
	 *
	 **/
	void setX(int value);

	/**
	 * @brief Gets the y axis
	 *
	 *
	 **/
	int getY(void) const;

	/**
	 * @brief Sets the y axis
	 *
	 *
	 **/
	void setY(int value);

	/**
	 * @brief Gets the horizontal velocity
	 *
	 *
	 **/
	int getVelX(void) const;

	/**
	 * @brief Sets the horizontal velocity
	 *
	 *
	 **/
	void setVelX(int value);

	/**
	 * @brief Gets the vertical velocity
	 *
	 *
	 **/
	int getVelY(void) const;

	/**
	 * @brief Sets the vertical velocity
	 *
	 *
	 **/
	void setVelY(int value);

	/**
	 * @brief Get the previous X axis position
	 *
	 *
	 **/
	int getPreviousX(void) const;

	/**
	 * @brief Get the previous Y axis position
	 *
	 *
	 **/
	int getPreviousY(void) const;

	/**
	 * @brief Return to last position
	 *
	 * It has the same effect than:
	 * @code
	 *    actor.x(actor.previousX());
	 *    actor.y(actor.previousY());
	 * @endcode
	 **/
	void goLastPosition();

	/**
	 * @brief Checks if it collides with other object
	 *
	 * The default behavor is just check if the bounding box
	 *  of one has intersection with the other. It can be overriden
	 *  to add a more accurate algorithm, like vector mask or pixel perfect.
	 **/
	virtual bool collides(const Actor &other) const;

	/**
	 * @brief Actualizes the actor's position
	 *
	 * This shouldn't be used by actor itself, but called from
	 *  the EventHandler that owns it.
	 **/
	void doFrame(void);

	/**
	 * @brief Draws the actor on the given surface
	 *
	 * It can be called any time it's necessary. In other words
	 *  it's not guaranted that it will be sinchronized with frame.
	 *  The default behavor is just show the associated sprite, but
	 *  it can be changed by overrideing it.
	 **/
	virtual void draw(SDL_Surface *dst);

	/**
	 * @brief Draw The sprite on current position.
	 *
	 * Useful when you redefine the drawHandler, but still want to
	 *  show the sprite on current position (the default behavior).
	 * @param dst the destiny surface.
	 */
	void drawSprite(SDL_Surface *dst);

	/**
	 * @brief Draw The sprite on current position.
	 *
	 * Useful when you redefine the drawHandler and want to show the
	 *  sprite on screen in arbitrary position
	 * @param dst the destiny surface.
	 * @param x the x position.
	 * @param y the y position.
	 */
	void drawSprite(SDL_Surface *dst, int x, int y);

protected:
	////////////////////////////////
	// Event Handling Definitions //
	////////////////////////////////
	/**
	 * @brief Connect a method as an frame event handler.
	 * @param handler the handler. Use like the example code:
	 * @code
	 * connectFrameEvent((mpFrameHandler)myHandlerMethod);
	 * @endcode
	 * You generally would prefer use the Wolf_HandleFrameEvent macro:
	 * @code
	 * Wolf_HandleFrameEvent(myClass,myHandlerMethod);
	 * @endcode
	 */
	void connectFrameEvent(mpFrameHandler handler);

	/**
	 * @brief stop handling frame events.
	 */
	void disconnectFrameEvent();

	/**
	 * @brief Connect a method as an frame start event handler.
	 */
	void connectStartFrameEvent(mpFrameHandler handler);

	/**
	 * @brief stop handling start frame events.
	 */
	void disconnectStartFrameEvent();

	/**
	 * @brief Connect a method as an frame start event handler.
	 */
	void connectEndFrameEvent(mpFrameHandler handler);

	/**
	 * @brief stop handling start frame events.
	 */
	void disconnectEndFrameEvent();

	/**
	 * @brief Connect a method as an draw event handler.
	 *
	 * The default drawing behavior (i.e. draw the sprite on current position) is
	 *  switched by the behavior defined on the handler method. If you want to
	 *  preserve the default behavior, call drawSprite(SDL_Surface*) or drawSprite(SDL_Surface*,int,int)
	 *  inside your drawHandler.
	 */
	void connectDrawEvent(mpDrawHandler handler);

	/**
	 * @brief stop handling draw events.
	 */
	void disconnectDrawEvent();

	/**
	 * @brief Connect a method as an collision event handler.
	 */
	void connectCollisionEvent(mpCollisionHandler handler);

	/**
	 * @brief stop handling collision events.
	 */
	void disconnectCollisionDrawEvent();
private:
	Sprite sprite;
	int x;
	int y;
	int previousX;
	int previousY;
	int velX;
	int velY;
	bool pixelCollision;
	mpDrawHandler drawHandler;

	///Inits. Because current version of my compiler still don't support inter constructor calling
	void _init(int x_, int y_);
};
//Event definition helpers. Must be called inside the constructor of Actor classes
#define Wolf_HandleFrameEvent(className,methodName) connectFrameEvent(reinterpret_cast<mpFrameHandler>(&className::methodName))
#define Wolf_HandleStartFrameEvent(className,methodName) connectStartFrameEvent(reinterpret_cast<mpFrameHandler>(&className::methodName))
#define Wolf_HandleEndFrameEvent(className,methodName) connectEndFrameEvent(reinterpret_cast<mpFrameHandler>(&className::methodName))
#define Wolf_HandleDrawEvent(className,methodName) connectDrawEvent(reinterpret_cast<mpDrawHandler>(&className::methodName))
#define Wolf_handleCollisionEvent(className, methodName) connectCollisionEvent(reinterpret_cast<mpCollisionHandler>(&className::methodName))

}

#endif // WOLF_ACTOR_HPP
